package com.ebs.md.impl.payload.orderbook;

/**
 * Perforce Revision:
 *  $File: //depot/MarketData/commons-md-payload-impl/MAIN/src/main/java/com/ebs/md/impl/payload/orderbook/SnapshotSideBookDetailedEntryAdapter.java $
 *  $Revision: #4 $  $Change: 115126 $  $DateTime: 2012/09/23 15:15:49 $  $Author: Aviad.Yosef $
 */

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;

import com.ebs.commons.comp.IParsedPayload;
import com.ebs.md.api.BasicSnapshot;
import com.ebs.md.api.BookSide;
import com.ebs.md.api.IDetailedBookEntry;
import com.ebs.md.api.ISnapshot;
import com.ebs.md.api.OrderSide;
import com.ebs.md.api.Rate;
import com.ebs.md.api.abs.AbsProductProvider;
import com.ebs.md.api.abs.BasicDetailedBookEntry;
import com.ebs.md.api.orderbook.ISnapshotSideAdapter;
import com.ebs.md.api.product.PairProduct;


public class SnapshotSideBookDetailedEntryAdapter
implements  ISnapshotSideAdapter<IParsedPayload, IDetailedBookEntry>
{
	protected final static String PAYLOAD_FIELD_NAME_BOOK_ENTRIES_PREFIX = "MktQteInfo"; // if via MQ Adapter than value of it is List<IParsedPayload>
	protected final static String PAYLOAD_FIELD_NAME_MESSAGE_VERSION     = "msg_version";
	
	
	protected AbsProductProvider<String, PairProduct> pairProductProvider;
	
	public void setPairProductProvider(AbsProductProvider<String, PairProduct> pairProductProvider) {
		this.pairProductProvider = pairProductProvider;
	}

	
	public ISnapshot<IDetailedBookEntry> convert(IParsedPayload source) {
		PairProduct product = extractProduct(source);
		OrderSide   orderSide = OrderSide.byLegcayCode(source.getParsedIntField("qte_type"));
		BookSide    bookSide  = BookSide.fromOrderSide(orderSide);
		
		long   snapTimeMillis  = source.getParsedLongField("msg_timestamp_long", 0);
		int    msgVersion      = source.getParsedIntField(PAYLOAD_FIELD_NAME_MESSAGE_VERSION);
		
		Calendar snapTime = Calendar.getInstance(gmtTZ);
		snapTime.setTimeInMillis(snapTimeMillis);

		List<IDetailedBookEntry> beList; 
		
    	// should be MktQteInfo4 or MktQteInfo5
    	final String quotesPayloadNameFromMq = PAYLOAD_FIELD_NAME_BOOK_ENTRIES_PREFIX + msgVersion;
    	
    	Object mkt_qte_info = source.getParsedField(quotesPayloadNameFromMq);
		
		if ( mkt_qte_info != null ) {
			beList = extractPayloadFromMQ(bookSide, source, quotesPayloadNameFromMq);
		} else {
			beList = extractPayloadFromDB(bookSide, source);
		}
		
		return new BasicSnapshot<IDetailedBookEntry>(product, snapTime, bookSide, beList);
	}

	/**
	 * 
	 * @param bookSide
	 * @param payload
	 * @return
	 * 
	 * @see mq_msg_build.xml (in RDS, FXSM)
	 */
	protected List<IDetailedBookEntry> extractPayloadFromMQ(BookSide bookSide, IParsedPayload payload, String quotesPayloadName) {
		List<IDetailedBookEntry> bookSideColl   = new ArrayList<IDetailedBookEntry>(16);
		
		List<IParsedPayload> bookEntriesPayloadList = (List<IParsedPayload>)payload.getParsedField(quotesPayloadName);
		
		for ( IParsedPayload currBookEntryPayload : bookEntriesPayloadList ) {
			IDetailedBookEntry be = convertBookEntryFromMQ(currBookEntryPayload, bookSide);
			
			bookSideColl.add(be);
		}
		
		return bookSideColl;
	}
	
	/**
	 * 
	 * @param bookSide
	 * @param payload
	 * @return
	 * 
	 * @see DB Tables    : OB_SNAPSHOT_MAIN, OB_SNAPSHOT_DETAIL
	 * @see DB Functions : GET_HIST_OB
	 * @see DB Types     : ORDER_BOOK , ORDER_BOOK_ARRAY
	 */
	protected List<IDetailedBookEntry> extractPayloadFromDB(BookSide bookSide, IParsedPayload payload) {
		List<IDetailedBookEntry> bookSideColl   = new ArrayList<IDetailedBookEntry>(16);
		
		int arrays_size = payload.getParsedIntField("ob_size", 0);

		if ( arrays_size <= 0 ) {
			return bookSideColl;
		}
		
		String[] arr_rate   = castToArray(payload.getParsedField("prc_qte_value_array"));
		Number[] arr_amount = castToArray(payload.getParsedField("qte_amt_avail_result_array"));
		String[] arr_id     = castToArray(payload.getParsedField("ebs_trans_id_array"));

		Number[] arr_qte_time  = castToArray(payload.getParsedField("adjusted_timestamp_array"));
		String[] arr_deal_code = castToArray(payload.getParsedField("qte_floor_code_array"));
		String[] arr_trader_id = castToArray(payload.getParsedField("trader_id_array"));

		Number[] arr_total_amount   = castToArray(payload.getParsedField("qte_amt_result_array"));
		Number[] arr_display_amount = castToArray(payload.getParsedField("qte_amt_display_result_array"));

		String[] arr_trader_region = castToArray(payload.getParsedField("trader_region_array"));

		for ( int ix=0 ; ix < arrays_size ; ix++ ) {
			Rate rate = null;
			String strRate = safeExtractItemFromArray(arr_rate,   ix);
			if ( strRate != null ) {
				rate = new Rate(strRate);
			}
			Calendar quoteTime = null;
			Number timeMillisNumber = safeExtractItemFromArray(arr_qte_time, ix);
			if ( timeMillisNumber != null ) {
				quoteTime = Calendar.getInstance(gmtTZ);
				quoteTime.setTimeInMillis(timeMillisNumber.longValue());
			}
			
			BasicDetailedBookEntry be = new BasicDetailedBookEntry(
				  bookSide
				, rate
				, safeExtractItemFromArray(arr_amount,         ix, BigDecimal.ZERO).longValue()
				, safeExtractItemFromArray(arr_id,        ix)
				, quoteTime
				, safeExtractItemFromArray(arr_deal_code, ix)
				, safeExtractItemFromArray(arr_trader_id, ix)
				, safeExtractItemFromArray(arr_total_amount,   ix, BigDecimal.ZERO).longValue()
				, safeExtractItemFromArray(arr_display_amount, ix, BigDecimal.ZERO).longValue()
				, safeExtractItemFromArray(arr_trader_region, ix)				);

			bookSideColl.add(be);
		}

		return bookSideColl;
	}
	
	protected static <T> T[] castToArray(Object o) {
		if ( o == null ) {
			return null;
		} else {
			return (T[])o;
		}
	}
	
	protected static <T> T safeExtractItemFromArray(T[] arr, int itemIndex, T defaultValue) {
		if ( arr == null || arr.length <= 0 )
			return defaultValue;
		if ( arr.length < itemIndex + 1 ) {
			return defaultValue;
		}
		return arr[itemIndex];
	}
	
	protected static <T> T safeExtractItemFromArray(T[] arr, int itemIndex) {
		return safeExtractItemFromArray(arr,itemIndex,null);
	}
	
	static final protected TimeZone gmtTZ = TimeZone.getTimeZone("GMT");

	
	protected IDetailedBookEntry convertBookEntryFromMQ(IParsedPayload bePayload, BookSide bookSide) {
		Rate rate   = new Rate(
				      bePayload.getParsedStringField("prc_qte_value", "0.0"));
		long amount = bePayload.  getParsedLongField("qte_amt_avail_result", 0);
		String qteId= bePayload.getParsedStringField("ebs_trans_id");
		long total_amount   = bePayload.getParsedLongField("qte_amt_result", 0);
		long display_amount = bePayload.getParsedLongField("qte_amt_display_result", 0);

		String region       = bePayload.getParsedStringField("trader_region");
		
		BasicDetailedBookEntry dbe = new BasicDetailedBookEntry(bookSide, rate, amount, qteId, total_amount, display_amount, region);
		addExtraDetailsMQ(dbe, bePayload);
		return dbe;
	}
	
	protected void addExtraDetailsMQ(BasicDetailedBookEntry bdbe , IParsedPayload pp) {
		String dealCode = pp.getParsedStringField("qte_floor_code");
		String traderId = pp.getParsedStringField("trader_id");
		long   beTimeInMillis  = pp.getParsedLongField("adjusted_timestamp", 0);
		
		Calendar quoteTime = Calendar.getInstance(gmtTZ);
		quoteTime.setTimeInMillis(beTimeInMillis);
		
		bdbe.setExtraDetails(quoteTime, dealCode, traderId);
	}

	
	protected PairProduct extractProduct(IParsedPayload pp) {
		String  baseCcyIso  = pp.getParsedStringField ( "base_ccy");
		String localCcyIso  = pp.getParsedStringField ("local_ccy");
		int    matchingType = pp.getParsedIntField    ("matching_type");
		
		String productKey = PairProduct.generateKey(baseCcyIso, localCcyIso, matchingType);
		
		PairProduct cachedProduct = pairProductProvider.getProduct(productKey);
		
		if ( cachedProduct == null ) {
			// caching it
			cachedProduct = new PairProduct(baseCcyIso, localCcyIso, matchingType);
			
			pairProductProvider.storeKnownItem(productKey, cachedProduct);
		}
		return cachedProduct;
	}

}
